using System;
using System.Collections.Generic;

namespace Core.Step
{
    /// <summary>
    /// 负责管理步骤的类，提供对步骤的添加、跳转和执行控制。
    /// </summary>
    public class StepSystem
    {
        // 存储所有步骤的列表
        private readonly List<StepBase> _steps;

        // 当前步骤
        public StepBase Current { private set; get; }

        // 当前步骤索引
        public int CurrentIndex { private set; get; } = -1;

        public event Action<StepBase> OnRefresh; // 更新步骤事件
        public event Action<float> OnRefreshProgress; // 更新进度事件
        public event Action OnDone; // 所有步骤完成事件
        public event Action OnBegin; // 步骤开始
        public event Action OnStop; //结束时

        public StepSystem(StepBase[] steps)
        {
            _steps = new List<StepBase>(steps);
            _steps.Sort((a, b) =>
            {
                int comparison = a.StepNumber.CompareTo(b.StepNumber);
                return comparison == 0 ? a.SubNumber.CompareTo(b.SubNumber) : comparison;
            });
            foreach (StepBase step in _steps)
            {
                step.OnCompleted += NextStep;
            }
        }

        /// <summary>
        /// 开始管理步骤，初始化所有步骤并跳转到第一个步骤。
        /// </summary>
        public void Begin()
        {
            if (_steps.Count == 0)
                throw new NullReferenceException("Steps is empty.");

            CurrentIndex = 0;
            Goto(CurrentIndex);
            OnBegin?.Invoke();
        }

        /// <summary>
        /// 结束管理步骤，退出当前步骤并清空所有步骤。
        /// </summary>
        public void Stop()
        {
            Current?.Exit();
            Current = null;
            OnStop?.Invoke();
        }

        /// <summary>
        /// 根据索引获取步骤。
        /// </summary>
        /// <param name="index">步骤索引。</param>
        /// <returns>返回指定索引的步骤。</returns>
        /// <exception cref="ArgumentOutOfRangeException">当索引超出范围时抛出。</exception>
        public StepBase GetStep(int index)
        {
            if (index < 0 || index >= _steps.Count)
                throw new ArgumentOutOfRangeException(nameof(index), "Index out of range.");
            return _steps[index];
        }

        /// <summary>
        /// 根据步骤号和子步骤号获取步骤。
        /// </summary>
        /// <param name="stepNumber">步骤号。</param>
        /// <param name="subNumber">子步骤号。</param>
        /// <returns>返回指定步骤号和子步骤号的步骤。</returns>
        /// <exception cref="KeyNotFoundException">当未找到对应步骤时抛出。</exception>
        public StepBase GetStep(int stepNumber, int subNumber)
        {
            StepBase findStepBase = _steps.Find(s => s.StepNumber == stepNumber && s.SubNumber == subNumber);
            if (findStepBase == null)
                throw new KeyNotFoundException($"Step not found for StepNumber: {stepNumber}, SubNumber: {subNumber}.");
            return findStepBase;
        }

        /// <summary>
        /// 根据步骤号获取所有对应的步骤。
        /// </summary>
        /// <param name="stepNumber">步骤号。</param>
        /// <returns>返回对应步骤号的步骤数组。</returns>
        public StepBase[] GetSteps(int stepNumber)
        {
            return _steps.FindAll(s => s.StepNumber == stepNumber).ToArray();
        }

        /// <summary>
        /// 跳转到指定步骤号。
        /// </summary>
        /// <param name="stepNumber">步骤号。</param>
        /// <exception cref="ArgumentOutOfRangeException">当步骤号超出范围时抛出。</exception>
        public void Goto(int stepNumber)
        {
            if (stepNumber < 0 || stepNumber >= _steps.Count)
                throw new ArgumentOutOfRangeException(nameof(stepNumber), "Step number is out of range.");
            Current?.Exit(); // 退出当前步骤
            Current = _steps[CurrentIndex = stepNumber]; // 更新当前步骤
            Current.Enter(); // 进入新步骤
            OnRefresh?.Invoke(Current); // 更新步骤事件
            float progress = (float)(CurrentIndex + 1) / (float)_steps.Count;
            OnRefreshProgress?.Invoke(progress); // 更新进度事件
        }

        /// <summary>
        /// 跳转到指定的步骤号和子步骤号。
        /// </summary>
        /// <param name="stepNumber">步骤号。</param>
        /// <param name="subNumber">子步骤号。</param>
        /// <exception cref="KeyNotFoundException">当未找到对应步骤时抛出。</exception>
        public void Goto(int stepNumber, int subNumber)
        {
            int index = _steps.FindIndex(s => s.StepNumber == stepNumber && s.SubNumber == subNumber);
            if (index < 0)
                throw new KeyNotFoundException($"未找到‘{stepNumber}-{subNumber}’步骤，请检查...");
            Goto(index);
        }

        /// <summary>
        /// 跳转到指定的步骤对象。
        /// </summary>
        /// <param name="stepBase">步骤对象。</param>
        /// <exception cref="KeyNotFoundException">当未找到对应步骤时抛出。</exception>
        public void Goto(StepBase stepBase)
        {
            int index = _steps.IndexOf(stepBase);
            if (index < 0)
                throw new KeyNotFoundException("Step not found.");
            Goto(index);
        }

        /// <summary>
        /// 刷新当前步骤。 
        /// </summary>
        public void AfreshStep()
        {
            Current?.Exit();
            Current?.Enter();
        }

        /// <summary>
        /// 跳转到下一个步骤。
        /// </summary>
        public void NextStep()
        {
            int nextIndex = CurrentIndex + 1;
            if (nextIndex < _steps.Count)
            {
                Goto(nextIndex); // 跳转到下一个步骤
                return;
            }

            Current?.Exit(); // 退出当前步骤
            OnDone?.Invoke(); // 所有步骤完成事件
        }

        /// <summary>
        /// 跳转到上一个步骤。
        /// </summary>
        public void LastStep()
        {
            int lastIndex = CurrentIndex - 1;
            if (lastIndex >= 0)
            {
                Goto(lastIndex);
                return;
            }

            Current?.Exit();
        }

        ~StepSystem()
        {
            foreach (StepBase step in _steps)
            {
                step.OnCompleted -= NextStep;
            }
        }
    }
}